#ifndef ___fat32_FAT

#define ___fat32_FAT 1

#include "hora.c"
#include "f32_acon.c"
#include "f32_base.c"
#include "mtlib.c"
#include "operbits.c"

#include "f32_dfat.c"

void fat32_inicializar_FAT(unsigned long, unsigned long, unsigned char, unsigned char, unsigned long);
unsigned long fat32_elemento_FAT(unsigned long);
void fat32_establecer_elemento_FAT(unsigned long, unsigned long);
void fat32_actualizar_FAT(long double);
void fat32_actualizar_un_sector_FAT(unsigned int);
void fat32_enviar_actualizacion_FAT_a_discos(unsigned int, unsigned char, unsigned long);
void fat32_rellenar_sectores(void);
void fat32_procesar_datos_entrada_sector_FAT(unsigned int, void *);

void fat32_inicializar_FAT (unsigned long cantidadClusteresDisco, unsigned long inicioFAT, unsigned char copiaActiva, 
                            unsigned char cantidadCopias, unsigned long longitudCopia) {
  if (fat32_FAT.estado) return;
  fat32_FAT.cantSectores = ((cantidadClusteresDisco - 1) / 128) + 1;
  fat32_FAT.ultimaActualizacion = 0;
  fat32_FAT.posicionInicialDisco = inicioFAT;
  fat32_FAT.longitudDisco = longitudCopia;
  fat32_FAT.cantidadCopias = cantidadCopias;
  fat32_FAT.copiaActiva = copiaActiva;
  fat32_FAT.sectores = malloc(sizeof(struct fat32_sector_FAT) * fat32_FAT.cantSectores);
  fat32_rellenar_sectores();
}

unsigned long fat32_elemento_FAT (unsigned long numeroCluster) {
  if (!fat32_FAT.estado) return 1;
  if (numeroCluster >= (fat32_sectores.cantSectores * 128)) return 1;
  return fat32_FAT.sectores[numeroCluster / 128].componentes[numeroCluster % 128].numero;
}

void fat32_establecer_elemento_FAT (unsigned long numeroCluster, unsigned long valorNuevo) {
  if (!fat32_FAT.estado) return;
  if (numeroCluster >= (fat32_sectores.cantSectores * 128)) return;
  fat32_FAT.sectores[numeroCluster / 128].componentes[numeroCluster % 128].numero = valorNuevo & 268435455;  
  fat32_FAT.sectores[numeroCluster / 128].estado = 1;
}

void fat32_actualizar_FAT (long double tiempoDisponible) {
  unsigned int conexionDisponible;
  if (!fat32_FAT.estado) return;
  conexionDisponible = fat32_obtener_conexion_disponible();
  if (!conexionDisponible) return;
  long double horaFinal = hora() + tiempoDisponible;
  while (hora() < horaFinal) fat32_actualizar_un_sector_FAT(conexionDisponible);
  fat32_liberar_conexion_retenida(conexionDisponible);
}

void fat32_actualizar_un_sector_FAT (unsigned int conexion) {
  unsigned long sector = fat32_FAT.ultimaActualizacion ++;
  if (fat32_FAT.ultimaActualizacion == fat32_FAT.cantSectores) fat32_FAT.ultimaActualizacion = 0;
  if (!fat32_FAT.sectores[sector].estado) return;
  if (fat32_FAT.copiaActiva == 255) {
    unsigned char copiaActual;
    for (copiaActual = 0; copiaActual < fat32_FAT.cantidadCopias; copiaActual ++)
      fat32_enviar_actualizacion_FAT_a_discos(conexion, copiaActual, sector);
  } else
    fat32_enviar_actualizacion_FAT_a_discos(conexion, fat32_FAT.copiaActiva, sector);
  fat32_FAT.sectores[sector].estado = 0;
}

void fat32_enviar_actualizacion_FAT_a_discos (unsigned int conexion, unsigned char copiaFAT, unsigned long numeroSector) {
  char datosSector[512];
  unsigned int posicion;
  unsigned long valorTemp, n2;
  unsigned long sectorAbsoluto;
  for (posicion = 0; posicion < 128; posicion ++) {
    valorTemp = fat32_FAT.sectores[numeroSector].componentes[posicion].numero;
    n2 = fat32_FAT.sectores[numeroSector].componentes[posicion].reservado;
    valorTemp = establecerBits(valorTemp, 28, 4, n2);
    fb_convertir_a_little_endian(datosSector + (posicion * 4), valorTemp, 32);
  }
  sectorAbsoluto = fat32_FAT.posicionInicialDisco + fat32_FAT.longitudDisco * copiaFAT + numeroSector;
  mtf_enviar_mensaje(fat32_centro_mensajes, conexion, MTE_ESCRIBIR_SECTOR, (long) sectorAbsoluto, (void *) datosSector);
}

void fat32_rellenar_sectores (void) {
  unsigned int conexion = fat32_obtener_conexion_disponible();
  unsigned char copiaFAT = fat32_FAT.copiaActiva;
  if (copiaFAT == 255) copiaFAT = 0;
  unsigned long sectorBase = fat32_FAT.posicionInicialDisco + fat32_FAT.longitudDisco * copiaFAT;
  unsigned long posicionBaseActual, posActual;
  unsigned long sector;
  unsigned short sectoresRemanentes;
  char datosLeidos[512];
  char * datos = datosLeidos;
  mti_mensaje mensaje;
  for (posicionBaseActual = 0; posicionBaseActual < fat32_FAT.cantidadSectores; posicionBaseActual += 256) {
    sectoresRemanentes = fb_minimo(256, fat32_FAT.cantidadSectores - posicionBaseActual);
    for (posActual = 0; posActual < sectoresRemanentes; posActual ++) {
      sector = sectorBase + posicionBaseActual + posActual;
      mtf_enviar_mensaje(fat32_centro_mensajes, conexion, MTE_LEER_SECTOR, (long) sector);
    }
    while (sectoresRemanentes) {
      if (!mtf_recibir_mensajes(fat32_centro_mensajes)) continue;
      mensaje = mtf_siguiente_mensaje(fat32_centro_mensajes);
      if (mensaje.remitente != conexion || mensaje.categoria != MTA_MENSAJE || 
          (mensaje.tipo != MTR_DATOS_SECTOR && mensaje.tipo != MTR_NO_LEYO_SECTOR)) {
        mtf_descartar_mensaje(fat32_centro_mensajes, mensaje.codigo);
        continue;
      }
      if (mensaje.tipo == MTR_DATOS_SECTOR) {
        mtf_obtener_datos(fat32_centro_mensajes, mensaje.codigo, (long *) &sector, (void **) &datos);
        sector -= sectorBase;
        fat32_procesar_datos_entrada_sector_FAT(sector, datosLeidos);
        sectoresRemanentes --;
      } else
        mtf_enviar_mensaje(fat32_centro_mensajes, conexion, MTE_LEER_SECTOR, (long) sector);
      mtf_descartar_mensaje(fat32_centro_mensajes, mensaje.codigo);
    }
  }
  fat32_FAT.estado = 1;
}

void fat32_procesar_datos_entrada_sector_FAT (unsigned int sector, void * datos) {
  unsigned long int datosPosicion;
  unsigned int posActual;
  struct fat32_datos_elemento_FAT elem;
  char * datosSector = datos;
  for (posActual = 0; posActual < 128; posActual ++) {
    datosPosicion = fb_convertir_de_little_endian(datosSector + (4 * posActual), 32);
    elem.numero = obtenerBits(datosPosicion, 0, 28);
    elem.reservado = obtenerBits(datosPosicion, 28, 4);
    fat32_FAT.sectores[sector].componentes[posActual] = elem;
  }
  fat32_FAT.sectores[sector].estado = 0;
}

#endif